package net.lulihu.common_util.jdbc.template;

import net.lulihu.Assert;
import net.lulihu.ObjectKit.CollectionKit;
import net.lulihu.ObjectKit.StrKit;
import net.lulihu.common_util.jdbc.annotation.Column;
import net.lulihu.common_util.jdbc.annotation.InsertDescriptor;
import net.lulihu.common_util.jdbc.annotation.PkId;
import net.lulihu.common_util.jdbc.annotation.Table;
import net.lulihu.common_util.jdbc.model.InsertAnnotationDescriptor;
import net.lulihu.common_util.jdbc.model.TemplateParam;
import net.lulihu.exception.ToolBoxException;
import net.lulihu.functional.ConsumerArgsResult;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 插入语句模板生成对象
 * <p>
 * key-> 对象属性对象 Field  value-> 指定注解描述
 */
public class InsertSqlTemplate extends SqlTemplate<Field, InsertAnnotationDescriptor> {

    /**
     * 插入语句模板
     */
    private static final String template = "INSERT INTO `{}` ( {} ) VALUES {}";

    private static final String COMMA = StrKit.COMMA + " ";
    private static final int COMMA_LENGTH = COMMA.length();
    private static final String ANTI_SINGLE_QUOTE = "`";

    /**
     * 生成批量插入sql语句
     *
     * @param beans 多个对象
     * @return 数据模板
     */
    public TemplateParam batchInsertSqlTemplate(List<?> beans) {
        return generateInsertTemplateParam(beans,
                // 赋值表达式
                (values) -> {
                    Object value = values[0];
                    // 值为空，则设为默认值
                    if (value == null) {
                        InsertAnnotationDescriptor descriptor = (InsertAnnotationDescriptor) values[3];
                        // 主键判断
                        if (descriptor.getPrimaryKey()) {
                            throw new ToolBoxException("主键不自动递增时插入语句主键值不可以为空...");
                        }
                        value = descriptor.getDefaultValue();
                        // 序列化判断
                    } else if (ifSerializer((Field) values[1])) {
                        value = serializer(value);
                    }
                    return value;
                });
    }

    /**
     * 生成插入语句
     *
     * @param bean 数据库映射对象
     * @return 数据模板
     */
    public TemplateParam insertSqlTemplate(Object bean) {
        return generateInsertTemplateParam(Collections.singletonList(bean),
                // 赋值表达式
                (values) -> {
                    Object value = values[0];
                    // 空值判断
                    if (value == null) {
                        InsertAnnotationDescriptor descriptor = (InsertAnnotationDescriptor) values[3];
                        // 主键判断
                        if (descriptor.getPrimaryKey()) {
                            throw new ToolBoxException("主键不自动递增时插入语句主键值不可以为空...");
                        }
                        // 判断是否跳过
                        if (descriptor.getNullNotSave()) {
                            AtomicBoolean hasContinue = (AtomicBoolean) values[2];
                            // 设置跳过
                            hasContinue.set(true);
                        } else {
                            value = descriptor.getDefaultValue();
                        }
                        // 序列化判断
                    } else if (ifSerializer((Field) values[1])) {
                        value = serializer(value);
                    }
                    return value;
                });
    }


    /**
     * 生成插入模板参数
     *
     * @param beans              插入bean
     * @param consumerArgsResult 插入引用表达式,表达式参数为数组分别如下
     *                           {对象属性值【Object】，对象属性【Field】，
     *                           是否跳过该属性true 跳过反之不跳过【AtomicBoolean】，
     *                           属性注解描述【InsertAnnotationDescriptor】}
     * @return 插入模板参数对象 TemplateParam
     */
    private TemplateParam generateInsertTemplateParam(List<?> beans,
                                                      ConsumerArgsResult<Object, Object> consumerArgsResult) {
        Assert.isTrue(CollectionKit.isEmpty(beans), "映射对象不可以为空...");

        Class<?> beanClass = beans.get(0).getClass();

        Table table = getTable(beanClass);
        // 获取当前bean描述
        Map<Field, InsertAnnotationDescriptor> columnDescriptor = getColumnDescriptor(beanClass);

        int size = beans.size();
        int colSize = columnDescriptor.size();
        int len = colSize * size;

        Object[] tmpValues = new Object[len];
        List<String> cols = new ArrayList<>(colSize);
        int continueNum = 0;
        try {
            // 是否跳过本次循环 true 跳过反之不跳过
            AtomicBoolean hasContinue = new AtomicBoolean(false);
            int x = 0;
            for (Map.Entry<Field, InsertAnnotationDescriptor> entry : columnDescriptor.entrySet()) {
                Field field = entry.getKey();
                InsertAnnotationDescriptor descriptor = entry.getValue();

                for (int i = 0; i < size; i++) {
                    Object bean = beans.get(i);
                    Object value = field.get(bean);
                    // 结果设置与是否跳过判断
                    value = consumerArgsResult.accept(new Object[]{value, field, hasContinue, descriptor});
                    if (hasContinue.get()) {
                        continueNum++;
                        continue;
                    }
                    // 赋值
                    tmpValues[i * colSize + x] = value;
                }
                x++;
                // 跳过的列不保存
                if (!hasContinue.get()) cols.add(descriptor.getColumnName());
                hasContinue.set(false);
            }
        } catch (Exception e) {
            throw new ToolBoxException(e);
        }

        //去除空值
        Object[] values = new Object[len - continueNum];
        int y = 0;
        for (int i = 0; i < len; i++) {
            Object value = tmpValues[i];
            if (value != null) {
                values[y] = value;
                y++;
            }
        }

        String sqlStatement = generateInsertSqlStatement(table.value(), cols, size);
        return new TemplateParam(sqlStatement, values);
    }

    /**
     * 生成插入 Sql语句
     *
     * @param tableName 表名
     * @param cols      列名
     * @param size      插入行数
     * @return SqlStatement
     */
    private String generateInsertSqlStatement(String tableName, List<String> cols, int size) {

        StringBuilder colBuilder = new StringBuilder();
        StringBuilder valueBuilder = new StringBuilder();

        // 循环列名
        for (String col : cols) {
            colBuilder.append(COMMA).append(ANTI_SINGLE_QUOTE).append(col).append(ANTI_SINGLE_QUOTE);
        }
        colBuilder.delete(0, COMMA.length());

        // 循环值的占位符
        int coiSize = cols.size();
        for (int i = 0; i < size; i++) {
            valueBuilder.append(COMMA).append("( ");
            for (int j = 0; j < coiSize; j++) {
                valueBuilder.append("?").append(COMMA);
            }
            int length = valueBuilder.length();
            valueBuilder.delete(length - COMMA_LENGTH, length);
            valueBuilder.append(" )");
        }
        valueBuilder.delete(0, COMMA_LENGTH);

        // 生成sql命令
        return StrKit.format(template, tableName, colBuilder.toString(), valueBuilder.toString());
    }

    @Override
    public boolean hasContinue(Field field) {
        // 未定义列注解 跳过
        Column column = field.getAnnotation(Column.class);
        if (column == null) return true;
        // 定义为主键并且为自动递增跳过
        PkId pkId = field.getAnnotation(PkId.class);
        if (pkId != null) return pkId.autoIncrement();
        // 未定义插入注解跳过
        InsertDescriptor descriptor = field.getAnnotation(InsertDescriptor.class);
        return descriptor == null;
    }

    @Override
    public Map<Field, InsertAnnotationDescriptor> fieldCache() {
        return new LinkedHashMap<>();
    }

    @Override
    public void columnDescriptor(Map<Field, InsertAnnotationDescriptor> columnDescriptorMap, Field field) {
        // 保存字段属性描述
        InsertAnnotationDescriptor annotationDescriptor = new InsertAnnotationDescriptor();

        if (field.getAnnotation(PkId.class) != null) {
            annotationDescriptor.setPrimaryKey(true);
        } else annotationDescriptor.setPrimaryKey(false);

        InsertDescriptor descriptor = field.getAnnotation(InsertDescriptor.class);
        if (descriptor != null) {
            annotationDescriptor.setNullNotSave(descriptor.nullNotSave());
            annotationDescriptor.setDefaultValue(descriptor.defaultValue());
        }
        annotationDescriptor.setColumnName(field.getAnnotation(Column.class).value());
        columnDescriptorMap.put(field, annotationDescriptor);
    }

    /*---------------------------饿汉式单例*/
    private InsertSqlTemplate() {
        // 私有化构造函数
    }

    private static final InsertSqlTemplate insertSqlTemplate;

    static {
        insertSqlTemplate = new InsertSqlTemplate();
    }

    /**
     * 获取实例
     *
     * @return InsertSqlTemplate
     */
    public static InsertSqlTemplate getInstance() {
        return insertSqlTemplate;
    }

}
